#include "PF_handler.h"
#include "thread.h"
#include "vmm.h"
#include "interrupt.h"
#include "print.h"
#include "x86_disassemble.h"

struct x86_instruction_t x86i_struct;

uint32_t get_cr2()
{
   uint32_t cr2_value;
   
   // 内联汇编: 将 CR2 寄存器的值存入 cr2_value
   __asm__ volatile (
      "mov %%cr2, %0"    // 将 CR2 寄存器的值移动到 cr2_value
      : "=r"(cr2_value)  // 输出约束: 将结果存入 cr2_value
      :                   // 无输入约束
      : "memory"          // 告诉编译器我们可能修改内存
   );

   return cr2_value;
}
uint32_t get_cr3()
{
   uint32_t cr3_value;
   
   // 内联汇编: 将 CR2 寄存器的值存入 cr2_value
   __asm__ volatile (
      "mov %%cr3, %0"    // 将 CR2 寄存器的值移动到 cr2_value
      : "=r"(cr3_value)  // 输出约束: 将结果存入 cr2_value
      :                   // 无输入约束
      : "memory"          // 告诉编译器我们可能修改内存
   );

   return cr3_value;
}

void PF_handler(uint8_t vec_nr) {
   struct task_struct* cur_thread = running_thread();
   struct vcpu* vm_thread_cpu = (struct vcpu*)(cur_thread->guest_ptr);
   struct intr_stack* intr_stack = (struct intr_stack*)((*((uint32_t*)(0xc0000b0f))) - sizeof(struct intr_stack));
   // x86_dis(&x86i_struct);
   // // x86i_struct->i_addr = x86i_struct->start_i = (uint8_t*)intr_stack->eip;
   // // x86_dis(x86i_struct);
   // put_str("\n");
   uint32_t cr3_ = get_cr3();
   uint32_t cr2_ = get_cr2();
   if (cur_thread->guest_ptr) {
      // put_int((uint32_t)x86i_struct->start_i);
      if ((cr2_ & 0xffc00000) == 0xffc00000) {
         // put_str(" 1 ");
         // put_str("\n");
         asm volatile ("movl %0, %%cr3" : : "r" ((uint32_t)addr_v2p((uint32_t)vm_thread_cpu->vm_phy_page_table)) : "memory");
         switch (*((unsigned char*)(intr_stack->eip))) {
            case 0x8b:
            if ((cr2_ & 0x003ff000) == 0x003ff000) {
               intr_stack->eax = *(uint32_t*)(vm_thread_cpu->cr3 + (cr2_ & 0x00000fff));
            } else {
               intr_stack->eax = *(uint32_t*)((*((uint32_t*)(vm_thread_cpu->cr3 + (((cr2_ & 0x003ff000) >> 12) * 4))) & 0xfffff000) + (cr2_ & 0x00000fff));
            }
            // put_int((uint32_t)intr_stack->eax);
            intr_stack->eip += 2;
            break;

            case 0x89:
            if ((cr2_ & 0x003ff000) == 0x003ff000) {
               asm("jmp .");
               *(uint32_t*)(vm_thread_cpu->cr3 + (cr2_ & 0x00000fff)) = intr_stack->edx;
            } else {
               *(uint32_t*)((*((uint32_t*)(vm_thread_cpu->cr3 + (((cr2_ & 0x003ff000) >> 12) * 4))) & 0xfffff000) + (cr2_ & 0x00000fff)) = intr_stack->edx;

               asm volatile ("movl %0, %%cr3" : : "r" ((uint32_t)addr_v2p((uint32_t)vm_thread_cpu->vm_phy_page_table)) : "memory");
               uint32_t guest_phy_addr = intr_stack->edx;
               if (guest_phy_addr & 1) {
                  // put_int(guest_phy_addr);
                  uint32_t host_phy_addr = addr_v2p((guest_phy_addr & 0xfffff000));
                  asm volatile ("movl %0, %%cr3" : : "r" (cr3_) : "memory");
                  *((uint32_t*)(cr2_)) = host_phy_addr | PG_US_U | PG_RW_W | PG_P_1;
               } else {
                  asm("jmp .");
               }

            }
            // put_int((uint32_t)intr_stack->edx);
            intr_stack->eip += 2;
            break;

            case 0x0f:
            //asm("jmp .");
            switch (*((unsigned char*)(intr_stack->eip + 0x1))) {
               case 0xb6:
               if ((cr2_ & 0x003ff000) == 0x003ff000) {
                  intr_stack->edx =  (uint8_t)(*(uint8_t*)(vm_thread_cpu->cr3 + (cr2_ & 0x00000fff)));
               } else {
                  intr_stack->edx =  (uint8_t)(*(uint8_t*)((*((uint32_t*)(vm_thread_cpu->cr3 + (((cr2_ & 0x003ff000) >> 12) * 4))) & 0xfffff000) + (cr2_ & 0x00000fff)));
               }
               // put_int((uint32_t)intr_stack->edx);
               intr_stack->eip += 3;
               //asm("jmp .");
               break;
               default:
               put_str("error");
            }
            break;

            default:
            put_str("guest page fault");
         }
      } else {
         put_str("host_page_fault");
         asm("jmp .");
         
         
      }
   } else {
      put_str("host_page_fault");
      asm("jmp .");
   }
   // put_str("\n");
   asm volatile ("movl %0, %%cr3" : : "r" ((uint32_t)cr3_) : "memory");
   
}

